package ooo.reindeer.commons.utils;

import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public class XmlUtils {
	private XmlUtils() {
	}
	public static Map XML2MAP(String xml_path) throws InstantiationException, IllegalAccessException,
			ParserConfigurationException, SAXException, IOException {
		return XML2MAP(xml_path, ConcurrentHashMap.class);
	}
	public static Map XMLString2MAP(String xml) throws DOMException, ParserConfigurationException, SAXException,
			IOException, InstantiationException, IllegalAccessException {
		return XMLString2MAP(xml, ConcurrentHashMap.class);
	}

	public static Map MapOlny(Map map){

		Map<String,Object> result=new HashMap<String, Object>();

		MapOlny(map, result);

		return result;
	}

	public static String FileToXMLString(String path, String encoding) {
		InputStream input = null;
		try {
			StringBuffer sb = new StringBuffer();
//			try {
//				input = XmlUtils.class.getResourceAsStream(path);
//			} catch (Exception e) {
//            }
//			if (input == null) {
			try {
				input = new FileInputStream(path);
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
//			}
			InputStreamReader read = new InputStreamReader(input, encoding);
			BufferedReader bufferedReader = new BufferedReader(read);
			String lineTxt = null;
			while ((lineTxt = bufferedReader.readLine()) != null) {
				sb.append(lineTxt);
			}
			read.close();
			return sb.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}
	}

	private static void ListToMap(List entrys, Map valueMap) {
		for (int i = 0; i < entrys.size(); i++) {
			Object temp = entrys.get(i);

			if (temp instanceof Map) {
				Map valueMap1 = new HashMap();
				MapOlny((Map) temp, valueMap1);
				valueMap.put(String.valueOf(i), valueMap1);

			} else if (temp instanceof List) {
				Map valueMap1 = new HashMap();
				ListToMap((List) temp, valueMap1);
				valueMap.put(String.valueOf(i), valueMap1);
			} else if (temp instanceof String || temp instanceof Number) {
				Map valueMap1 = new HashMap();
				valueMap1.put("", temp.toString());
				valueMap.put(String.valueOf(i), valueMap1);
			} else {
				Map valueMap1 = new HashMap();
				valueMap1.put("", temp);
				valueMap.put(String.valueOf(i), valueMap1);
			}

		}
	}

	private static void MapOlny(Map map, Map<String, Object> result) {
		for (Object  o : map.entrySet()) {
			Entry<String , Object> entry= (Entry<String, Object>) o;

			if(entry.getValue() instanceof Map){

				Map valueMap=new HashMap();
				MapOlny((Map) entry.getValue(),valueMap);
				result.put(entry.getKey(),valueMap);

			}else if(entry.getValue() instanceof List){
				Map valueMap=new HashMap();
				ListToMap((List) entry.getValue(), valueMap);
				result.put(entry.getKey(),valueMap);
			}else if(entry.getValue() instanceof String || entry.getValue() instanceof Number){
				if (entry.getKey().isEmpty()) {
					result.put("", entry.getValue().toString());
				} else {
					Map valueMap = new HashMap();
					valueMap.put("", entry.getValue().toString());
					result.put(entry.getKey(), valueMap);
				}
			}else{

				if (entry.getKey().isEmpty()) {
					result.put("", entry.getValue());
				} else {
					Map valueMap = new HashMap();
					valueMap.put("", entry.getValue());
					result.put(entry.getKey(), valueMap);
				}


			}

		}
	}
	public static Map XMLString2MAP(String xml, Class<? extends Map> class_type) throws ParserConfigurationException,
			SAXException, IOException, DOMException, InstantiationException, IllegalAccessException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db;
		db = dbf.newDocumentBuilder();
		Document document = db.parse(new InputSource(new StringReader(xml)));
		NodeList employees = document.getChildNodes();
		return (Map) parserXml(employees, class_type);
	}
	public static String FileToXMLString(String path) {
		return FileToXMLString(path, "UTF-8");
	}

	public static Map XML2MAP(String xml_path, Class<? extends Map> class_type) throws ParserConfigurationException,
			SAXException, IOException, DOMException, InstantiationException, IllegalAccessException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db;
		InputStream input = null;
//		try {
//			input = XmlUtils.class.getResourceAsStream(xml_path);
//		} catch (Exception e) {
//        }
//		if (input == null) {
			try {
				input = new FileInputStream(xml_path);
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
//		}
		db = dbf.newDocumentBuilder();
		Document document = db.parse(input);
		NodeList employees = document.getChildNodes();
		return (Map) parserXml(employees, class_type);
	}
	@SuppressWarnings({ "unchecked" })
	private static Object parserXml(NodeList employees, Class<? extends Map> class_type)
			throws DOMException, InstantiationException, IllegalAccessException {
		Map map = class_type.newInstance();
		String value = null;
		for (int i = 0; i < employees.getLength(); i++) {
			Node employee = employees.item(i);
			if (employee.getNodeType() != 8)
				if (employee.getNodeType() == 1 || employee.getNodeValue() == null
						|| employee.getNodeValue().trim().isEmpty()) {
					if (employee.hasChildNodes() || employee.hasAttributes()) {
						Object sub_map = parserXml(employee.getChildNodes(), class_type);
						String name = employee.getNodeName();
						NamedNodeMap attributes = employee.getAttributes();
						for (int attribute_index = 0; attribute_index < attributes.getLength(); attribute_index++) {
							Node attribute = attributes.item(attribute_index);
							String attribute_nameString = attribute.getNodeName().trim();
							if (attribute_nameString.equalsIgnoreCase("name")) {
								name = (attribute.getNodeValue() == null || attribute.getNodeValue().trim().isEmpty())
										? name : attribute.getNodeValue().trim();
							} else {
								if (sub_map instanceof Map) {
									((Map) sub_map).put(attribute_nameString,
											(attribute.getNodeValue() == null
													|| attribute.getNodeValue().trim().isEmpty()) ? null
															: attribute.getNodeValue().trim());
								} else {
									String sub_value = (String) sub_map;
									sub_map = class_type.newInstance();
									((Map) sub_map).put(name, sub_value);
									((Map) sub_map).put(attribute_nameString,
											(attribute.getNodeValue() == null
													|| attribute.getNodeValue().trim().isEmpty()) ? null
															: attribute.getNodeValue().trim());
								}
							}
						}
						if (sub_map != null) {
							Object sub_map_list = map.get(name);
							if (sub_map_list == null) {
								map.put(name, sub_map);
							} else {
								if (!(sub_map_list instanceof List)) {
									sub_map_list = new ArrayList();
									((List) sub_map_list).add(map.get(name));
									map.put(name, sub_map_list);
								}
								((List) sub_map_list).add(sub_map);
							}
						}
					}
				} else {
					value = ((employee.getNodeValue().trim().isEmpty()) ? null : employee.getNodeValue().trim());
				}
		}
		if (map.size() <= 0 && value != null) {
			return value;
		} else {
			if (value != null) {
				map.put("", value);
			}
			return map;
		}
	}
	public static String map2Xml(Map map) {
		StringBuffer sbf = new StringBuffer();
		map2Xml(sbf, map);
		return xmlFormat(sbf.toString());
	}
	private static String xmlFormat(String xml) {
		StringWriter writer;
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db;
			db = dbf.newDocumentBuilder();
			Document doc = db.parse(new InputSource(new StringReader(xml)));
			DOMSource source = new DOMSource(doc);
			writer = new StringWriter();
			Result result = new StreamResult(writer);
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes");
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
			transformer.transform(source, result);
			return (writer.getBuffer().toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static void map2Xml(StringBuffer buffer, Map map) {
		Set set = map.entrySet();
		Iterator iterator = set.iterator();
		StringBuffer childSbf = new StringBuffer();
		while (iterator.hasNext()) {
			Entry e = (Entry) iterator.next();
			String key = (String) e.getKey();
			Object value = e.getValue();
			if (value instanceof List) {
				List list = (List) value;
				for (int j = 0; j < list.size(); j++) {
					childSbf.append("<").append(key).append(">");
					Map valueMap = (Map) list.get(j);
					map2Xml(childSbf, valueMap);
					childSbf.append("</").append(key).append(">");
				}
			} else if (value instanceof Map) {
				if (key != null) {
					childSbf.append("<").append(key).append(">");
				}
				Map valueMap = (Map) value;
				map2Xml(childSbf, valueMap);
				if (key != null) {
					childSbf.append("</").append(key).append(">");
				}
			} else {
				if (key != null) {
					childSbf.append("<").append(key).append(">");
				}
				if (null != value) {
					childSbf.append(value);
				} else {
				}
				if (key != null) {
					childSbf.append("</").append(key).append(">");
				}
			}
		}
		buffer.append(childSbf);
	}
	public static void main(String[] args) throws InstantiationException, IllegalAccessException,
			ParserConfigurationException, SAXException, IOException {
		Map map = XML2MAP("./conf/MTRouter.xml");
		System.out.println(map);
	}
}